package org.mzx.shortLink.adminn.service.impl;


import org.mzx.shortLink.start.designpattern.Chain.AbstractChainContext;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import org.mzx.shortLink.adminn.common.enums.userRegisterChainEnum;
import org.mzx.shortLink.starter.convention.Exception.ClientException;
import org.mzx.shortLink.starter.convention.Result.Result;
import org.mzx.shortLink.starter.web.Results;
import org.mzx.shortLink.adminn.domain.entity.TUser;
import org.mzx.shortLink.adminn.domain.mapper.TUserMapper;
import org.mzx.shortLink.adminn.dto.Request.LoginDtoReq;
import org.mzx.shortLink.adminn.dto.Request.TUserDtoReq;
import org.mzx.shortLink.adminn.dto.Request.TUserUpdateReq;
import org.mzx.shortLink.adminn.dto.Response.LoginDtoResp;
import org.mzx.shortLink.adminn.dto.Response.UserDto;
import org.mzx.shortLink.adminn.service.TGroupService;
import org.mzx.shortLink.adminn.service.TUserService;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static org.mzx.shortLink.adminn.common.enums.redisEnum.LOCK_USER_ZHUCE;
import static org.mzx.shortLink.adminn.common.enums.redisEnum.LOGIN_KEY;

/**
* @author mazhenxi
* @description 针对表【t_user】的数据库操作Service实现
* @createDate 2024-11-25 13:42:09
*/
@Service
@RequiredArgsConstructor
public class TUserServiceImpl extends ServiceImpl<TUserMapper, TUser>
    implements TUserService {

    private final RedissonClient redissonClient;

    private final StringRedisTemplate stringRedisTemplate;

    @Qualifier("userRegisterCachePenetrationBloomFilter")
    @Autowired
    private RBloomFilter<String> userRegisterCachePenetrationBloomFilter;

    private final TGroupService groupService;

    private final AbstractChainContext<TUserDtoReq> userRegisterChain;

    @Override
    public UserDto getByName(String userName) {
        TUser user = lambdaQuery().eq(TUser::getUsername, userName).one();
        if(user == null) {
            throw new ClientException("未查询到用户");
        }
        UserDto userDto = new UserDto();
        BeanUtils.copyProperties(user,userDto);
        return userDto;
    }

    /**
     * 需要同时改两张表所以需要加事务处理
     * @param user
     * @return
     */
    @Override
    @Transactional
    public Boolean zhuCe(TUserDtoReq user) {
        //进行责任链校验，判断
        userRegisterChain.handler(userRegisterChainEnum.mark,user);
        //分布式锁解决同一个id短时间大量请求注册的问题，上一个分布式锁
        RLock lock = redissonClient.getLock(LOCK_USER_ZHUCE + user.getUsername());
        try{
            boolean tryLock = lock.tryLock();
            if(tryLock) {
                TUser tUser = new TUser();
                BeanUtils.copyProperties(user,tUser);
               save(tUser);
               //如果用户注册成功了，那么在分组中为这个用户创建一个默认分组
                groupService.createGroup("默认分组",user.getUsername());
                //在布隆过滤器中加入用户的id
                userRegisterCachePenetrationBloomFilter.add(user.getUsername());
            }
        }catch (Exception e){
            throw new ClientException(e.getMessage());
        }finally {
            lock.unlock();
        }
        return true;
    }

    /**
     * 这里是判断这个id能不能用，就是如果id存在就返回false，不存在返回true
     * */
    @Override
    public Boolean isExist(String userName) {
        System.out.println("进入isExist方法");

        return !userRegisterCachePenetrationBloomFilter.contains(userName);
    }

    @Override
    public Void Update(TUserUpdateReq requestParam) {
        TUser tUser = new TUser();
        BeanUtils.copyProperties(requestParam,tUser);
        boolean update = lambdaUpdate().eq(TUser::getUsername, requestParam.getUsername())
                .eq(TUser::getDelFlag, 0)
                .update(tUser);
        if(!update) {
            throw new ClientException("更新失败");
        }
        return null;
    }

    @Override
    public LoginDtoResp login(LoginDtoReq requestParam) {
        //这里可以添加有一个登录检查责任链，检查密码是否有sql注入的问题
        //先检查登录信息
        String key = LOGIN_KEY+requestParam.getUsername();
        String token = (String) stringRedisTemplate.opsForHash().get(key, "token");
        //如果用户已经登录过了，那么从缓存中获取信息
        if(StringUtil.isNotEmpty(token)){
            return new LoginDtoResp(token);
        }

        //检查用户名和密码
        TUser user = lambdaQuery().eq(TUser::getUsername, requestParam.getUsername())
                .eq(TUser::getPassword, requestParam.getPassword())
                .eq(TUser::getDelFlag, 0)
                .one();
        if (user==null) {
            throw new ClientException("登陆失败用户名或密码错误");
        }

        //检查完毕
        //获取uuid当作token
        UUID uuid = UUID.randomUUID();
        //将数据插入到redis中，此redis的结构为hash，有两个元素，一个是token，另一个是json字符串
        String jsonStr = JSONUtil.toJsonStr(user);
        stringRedisTemplate.opsForHash().put(key,"token",uuid.toString());
        stringRedisTemplate.opsForHash().put(key,"value",jsonStr);
        stringRedisTemplate.expire(key,7L, TimeUnit.DAYS);
        //读取用户是否是VIP，远程调用VIP模块，这里需要改造一下微服务，



        return new LoginDtoResp(uuid.toString());
    }

    @Override
    public Void loginOut(String username) {
        //直接删除缓存即可
        stringRedisTemplate.delete(LOGIN_KEY+username);
        return null;
    }

}




